home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 1.toast / Sample Code / Devices and Hardware / Drivers / AsyncDriverSample / DiskImageCore.p < prev    next >
Encoding:
Text File  |  2000-09-28  |  16.1 KB  |  505 lines  |  [TEXT/CWIE]

  1. unit DiskImageCore;
  2.  
  3. (*
  4.     File:        DiskImageCore.p
  5.  
  6.     Contains:    Core code for the disk image device driver.
  7.  
  8.     Written by:    Quinn "The Eskimo!"
  9.  
  10.     Copyright:    © 1996 by Apple Computer, Inc., all rights reserved.
  11.  
  12.     Change History (most recent first):
  13.  
  14.     You may incorporate this sample code into your applications without
  15.     restriction, though the sample code has been provided "AS IS" and the
  16.     responsibility for its operation is 100% yours.  However, what you are
  17.     not permitted to do is to redistribute the source as "DSC Sample Code"
  18.     after having made changes. If you're going to re-distribute the source,
  19.     we require that you make it clear in the source that the code was
  20.     descended from Apple Sample Code, but that you've made changes.
  21. *)
  22.  
  23. interface
  24.  
  25.     uses
  26.         Types,
  27.         AppleTalk,
  28.         Files,
  29.         Devices;
  30.     
  31. {$PUSH}
  32. {$ALIGN MAC68K}
  33.  
  34.     (* ***** DiskImageRecord ***** *)
  35.     
  36.     (* DiskImageRecord is the core data structure of the driver.  It's a record
  37.         extension of a drive queue element, with the drive queue element data
  38.         structures at the front and the DiskImageCore data structures after that.
  39.         So fields are controlled by this module, and some by the AysncDriverSample
  40.         module.  This goes against modularity a bit, but it makes life a lot easier.
  41.     *)
  42.     
  43.     type
  44.         DiskImageRecord =
  45.             record
  46.                 (* The following fields make up a drive queue element and it's corresponding
  47.                     flags.  See "IM: Files", p2-84 for a description of each of the components.
  48.                 *)
  49.                 
  50.                 (* The following 4 bytes must be in order and immediately in front of driveQElement
  51.                     because the system expects them to be there.
  52.                 *)
  53.                 driveFlags: SignedByte;
  54.                 diskInPlace: SignedByte;
  55.                 driveInstalled: SignedByte;
  56.                 numberOfSides: SignedByte;
  57.                 driveQElement: DrvQEl;
  58.                 
  59.                 (* The remaining fields are an extension of the drive queue element that
  60.                     holds all of our per-drive variables.
  61.                 *)
  62.  
  63.                 diskSize: longint;                (* The size of the disk in bytes. *)
  64.  
  65.                 aspSessionNumber : integer;        (* The ASP session number to the server holding the disk. *)
  66.                 afpForkRef : integer;            (* The AFP fork refnum to the image file. *)
  67.  
  68.                 (* driverDCE must immediately preceed xppParam because the GenericCompletion
  69.                     and the PascalCompletion routines in AsyncDriverSample relies on it.
  70.                 *)
  71.                 driverDCE : DCtlPtr;
  72.                 
  73.                 (* Allocate one XPPParamBlock and AFP command and result blocks per drive,
  74.                     which is excessive but safe.  In general Mac DRVRs are single threaded,
  75.                     ie can only be handling one request at a time, so you would only need
  76.                     one copy of these things.
  77.                    Unfortunately we can't allocate these globally because the
  78.                     disk mounting call is done using an immediate call, which is not
  79.                     mutually excluded vis-a-vis outstanding asynchronous calls.  So we
  80.                     could hit this situation:
  81.                     
  82.                     1.    System starts async request to Drive A.
  83.                     2.    Before that request completes, someone asks to mount a new disk
  84.                         image using an immediate call, which scrunges the globally
  85.                         allocated XPPParamBlock.
  86.  
  87.                    There are ways I could get around this, such as allocating a temporary
  88.                     XPPParamBlock during the disk mount operation, but this was the cleanest
  89.                     (and easiest :) solution.
  90.                 *)
  91.                 
  92.                 xppParam : XPPParamBlock;
  93.                 commandBlock : packed array [0..1023] of Byte;
  94.                 resultBlock : packed array [0..1023] of Byte;
  95.             end;
  96.         DiskImageRecordPtr = ^DiskImageRecord;
  97.  
  98. {$ALIGN RESET}
  99. {$POP}
  100.  
  101.     (* ***** External Routines ***** *)
  102.  
  103.     function InitDiskImageCore : OSErr;
  104.         (* Initialise this module. *)
  105.  
  106.     function CreateDiskImage(diskImage : FSSpec; var disk : DiskImageRecordPtr) : OSErr;
  107.         (* Creates a DiskImageRecord for accessing the specification diskImage file.
  108.             This call must be made at SystemTask time.
  109.         *)
  110.  
  111.     function ReadDiskImage(disk : DiskImageRecordPtr;
  112.                             offset : longint; count : longint; buffer : Ptr;
  113.                             ioCompletion : ProcPtr) : OSErr;
  114.         (* Reads count bytes from a disk, at the given offset into the image,
  115.             and puts it in the buffer.  If ioCompletion is nil, the
  116.             routine operates synchronously.  If ioCompletion is not nil,
  117.             the routine operates asynchronously and calls ioCompletion
  118.             when it's done.
  119.            This call may be made at interrupt time, as long as it's made
  120.             asynchronously.
  121.         *)
  122.  
  123.     function DestroyDiskImage(disk : DiskImageRecordPtr) : OSErr;
  124.         (* Disposes a DiskImageRecord created by CreateDiskImage.
  125.             This call must be made at SystemTask time.
  126.         *)
  127.  
  128. implementation
  129.  
  130.     uses
  131.         OSUtils,
  132.         AppleTalk,
  133.         Devices,
  134.         Memory,
  135.         Errors,
  136.         FSM;
  137.  
  138.     (* ***** Disk Copy Files ***** *)
  139.     
  140.     (* DiskCopyHeader defines the header for a DiskCopy disk image, ie file type 'dImg'. *)
  141.  
  142. {$PUSH}
  143. {$ALIGN MAC68K}
  144.  
  145.     type
  146.         DiskCopyHeader =
  147.             record
  148.                 name: Str63;
  149.                 dataSize: longint;
  150.                 tagSize: longint;
  151.                 dataChecksum: longint;
  152.                 tagChecksum: longint;
  153.                 unknown: longint;
  154.             end;
  155.  
  156. {$ALIGN RESET}
  157. {$POP}
  158.  
  159.     (* ***** Borrowing an AFP Session Number ***** *)
  160.     
  161.     (* The following code was stolen in toto from "Technote NW 16 -- Borrowed AFP
  162.         Sessions" <http://devworld.apple.com/dev/technotes/nw/nw_16.html>.
  163.         You should read that for an explanation of what it does.
  164.     *)
  165.  
  166. {$PUSH}
  167. {$ALIGN MAC68K}
  168.  
  169.     CONST
  170.       { AFP version numbers }
  171.       AFPVer1_1 = 1;  { AFP version 1.1 }
  172.       AFPVer2_0 = 2;  { AFP version 2.0 }
  173.       AFPVer2_1 = 3;  { AFP version 2.1 }
  174.  
  175.       AFPSVolInfo = 124;  { server volume information call }
  176.  
  177.     TYPE
  178.       GetVolSessInfoRec = RECORD
  179.           sessAFPVersion: Integer;        {AFP version number}
  180.           sessReferenceNumber: Integer;   {session reference number}
  181.           sessAFPVolID: Integer;          {AFP volume identifier}
  182.           sessServerAddress: AddrBlock;   {server internet address}
  183.           sessUAMType: Integer;           {user authentication method}
  184.           sessUserNamePtr: StringPtr;     {ptr to user name string}
  185.           sessVolIconPtr: Ptr;            {ptr to server volume icon/mask}
  186.           sessWhereStringPtr: StringPtr;  {ptr to "where" information string}
  187.         END;
  188.  
  189. {$ALIGN RESET}
  190. {$POP}
  191.  
  192.     FUNCTION GetVolSessionInfo (theVRefNum: Integer;
  193.                  VAR theVolSessInfoRec: GetVolSessInfoRec): OSErr;
  194.       CONST
  195.         TSigWord = $4244; { HFS volume signature }
  196.       VAR
  197.         pb: ParamBlockRec;
  198.         aVCB: VCBPtr;
  199.         afpTranslatorRefNum: Integer;
  200.         err: OSErr;
  201.     BEGIN
  202.       { get the .AFPTranslator driver refNum }
  203.       err := OpenDriver('.AFPTranslator', afpTranslatorRefNum);
  204.       IF err <> noErr THEN
  205.         BEGIN { couldn't open the driver }
  206.           GetVolSessionInfo := err;
  207.           Exit(GetVolSessionInfo);
  208.         END;
  209.  
  210.       { find the VCB with the volume reference number }
  211.       aVCB := VCBPtr(GetVCBQHdr^.qHead);  { pointer to first VCB }
  212.       WHILE (aVCB <> NIL) DO
  213.         BEGIN
  214.           IF aVCB^.vcbSigWord = TSigWord THEN { must be HFS volume }
  215.             IF aVCB^.vcbVRefNum = theVRefNum THEN
  216.               Leave;  { we found the VCB }
  217.           aVCB := VCBPtr(aVCB^.qLink);  { next VCB }
  218.         END;
  219.       IF (aVCB = NIL) THEN
  220.         BEGIN  { couldn't find the volume }
  221.           GetVolSessionInfo := nsvErr;
  222.           Exit(GetVolSessionInfo);
  223.         END;
  224.  
  225.       { make the status call to get the volume session info }
  226.       WITH pb DO
  227.         BEGIN
  228.           ioRefNum := afpTranslatorRefNum;
  229.           csCode := AFPSVolInfo;
  230.           ioMisc := Ptr(aVCB);
  231.           ioBuffer := @theVolSessInfoRec;
  232.           ioReqCount := LongInt(sizeof(GetVolSessInfoRec));
  233.         END;
  234.       GetVolSessionInfo := PBStatusSync(@pb);
  235.     END;
  236.  
  237.     (* ***** Globals ****** *)
  238.  
  239.     var
  240.         gXPPRefNum : integer;            (* Driver refNum of the XPP driver. *)
  241.  
  242.     (* ***** AFP Command Build and Execute ****** *)
  243.         
  244.     (* The following routines are simple utilites to facility the building
  245.         and execution of AFP commands.  The XPP driver allows you to send
  246.         AFP commands, but that's about it.  It doesn't provide a decent
  247.         high-level API.  So, when you build commands, you have to ensure
  248.         that they map byte-for-byte to the command on the wire.  The
  249.         easiest way to do this is to build the commands one byte at a time,
  250.         rather than try to match your structures to structure of the command.
  251.     *)
  252.     
  253.     procedure StartAFPCommand(disk : DiskImageRecordPtr; cmdByte : integer);
  254.         (* Called when starting a new AFP command.  cmdByte is the command
  255.             we're starting.
  256.         *)
  257.     begin
  258.         disk^.commandBlock[0] := cmdByte;
  259.         disk^.xppParam.cbSize := 1;
  260.         disk^.xppParam.cbPtr := @disk^.commandBlock;
  261.     end; (* StartAFPCommand *)
  262.     
  263.     procedure AddAFPByte(disk : DiskImageRecordPtr; byte : integer);
  264.         (* Add a single byte to the current AFP command. *)
  265.     begin
  266.         disk^.commandBlock[disk^.xppParam.cbSize] := byte;
  267.         disk^.xppParam.cbSize := disk^.xppParam.cbSize + 1;
  268.     end; (* AddAFPByte *)
  269.     
  270.     procedure AddAFPWord(disk : DiskImageRecordPtr; word : integer);
  271.         (* Add a two byte word to a command, high byte first. *)
  272.     begin
  273.         disk^.commandBlock[disk^.xppParam.cbSize] := band(bsr(word, 8), $0FF);
  274.         disk^.commandBlock[disk^.xppParam.cbSize + 1] := band(word, $0FF);
  275.         disk^.xppParam.cbSize := disk^.xppParam.cbSize + 2;
  276.     end; (* AddAFPWord *)
  277.     
  278.     procedure AddAFPLong(disk : DiskImageRecordPtr; long : longint);
  279.         (* Add a four byte long to a command, high byte first. *)
  280.     begin
  281.         disk^.commandBlock[disk^.xppParam.cbSize] := band(bsr(long, 24), $0FF);
  282.         disk^.commandBlock[disk^.xppParam.cbSize + 1] := band(bsr(long, 16), $0FF);
  283.         disk^.commandBlock[disk^.xppParam.cbSize + 2] := band(bsr(long, 8), $0FF);
  284.         disk^.commandBlock[disk^.xppParam.cbSize + 3] := band(long, $0FF);
  285.         disk^.xppParam.cbSize := disk^.xppParam.cbSize + 4;
  286.     end; (* AddAFPLong *)
  287.     
  288.     procedure AddAFPString(disk : DiskImageRecordPtr; str : Str255);
  289.         (* Add a Pascal string to a command.  In AFP, Pascal strings are
  290.             packed in their minimal size.
  291.         *)
  292.     begin
  293.         BlockMove(@str[0], @disk^.commandBlock[disk^.xppParam.cbSize], length(str) + 1);
  294.         disk^.xppParam.cbSize := disk^.xppParam.cbSize + length(str) + 1;
  295.     end; (* AddAFPString *)
  296.     
  297.     function RunAFPCommand(disk : DiskImageRecordPtr; ioCompletion : ProcPtr) : OSErr;
  298.         (* Execute the current AFP command synchronously or asynchronously. *)
  299.         const
  300.             kStandardASPTimeout = 5;
  301.         var
  302.             err : OSStatus;
  303.     begin
  304.         (* Fill out the param block. *)
  305.         disk^.xppParam.ioCompletion := ioCompletion;
  306.         disk^.xppParam.ioRefNum := gXPPRefNum;
  307.         disk^.xppParam.csCode := afpCall;
  308.         disk^.xppParam.sessRefnum := disk^.aspSessionNumber;
  309.         disk^.xppParam.aspTimeout := kStandardASPTimeout;
  310.         
  311.         (* Run the command. *)
  312.         err := AFPCommand(@disk^.xppParam, ioCompletion <> nil);
  313.         
  314.         (* If we're running synchronously, ignore the error from the command
  315.             and return ioInProgress (ie 1).
  316.         *)
  317.         if ioCompletion <> nil then begin
  318.             err := ioInProgress;
  319.         end; (* if *)
  320.  
  321.         (* If we don't have an error, get the error out of the XPP paramblock.
  322.             Note that, because of the above check, this only happens if
  323.             we're running synchronously.
  324.         *)
  325.         if err = noErr then begin
  326.             err := disk^.xppParam.cmdResult;
  327.         end; (* if *)
  328.         
  329.         RunAFPCommand := err;
  330.     end; (* RunAFPCommand *)
  331.     
  332.     (* ***** Initialisation ***** *)
  333.     
  334.     function InitDiskImageCore : OSErr;
  335.         (* See comment in implementation part. *)
  336.         var
  337.             err : OSErr;
  338.     begin
  339.         err := OpenDriver('.XPP', gXPPRefNum);
  340.         InitDiskImageCore := err;
  341.     end; (* InitDiskImageCore *)
  342.     
  343.     (* ***** AFP Open/Close ****** *)
  344.     
  345.     (* Routines for issuing AFP commands to open and/or close a fork. *)
  346.     
  347.     function OpenAFPFork(disk : DiskImageRecordPtr; volumeID : integer;
  348.                                                                                                         parID : longint;
  349.                                                                                                         name : Str63) : OSErr;
  350.         (* Opens the data fork of the file an AFP server.  Note that parID
  351.             is derived from a local FSSpec, making the implicit
  352.             assumption the AFP "Directory ID" field maps 1-1 with
  353.             the parID in the FSSpec returned by the local AFP client
  354.             software.  Not doing this would be bizarre, but possible.
  355.            This call operates synchronously.
  356.         *)
  357.         var
  358.             err : OSStatus;
  359.     begin
  360.         (* Build the AFP command. *)
  361.         StartAFPCommand(disk, afpOpenFork);
  362.         AddAFPByte(disk, 0);            (* "Rsrc/Data Flag" = data fork *)
  363.         AddAFPWord(disk, volumeID);        (* "Volume ID" *)
  364.         AddAFPLong(disk, parID);        (* "Directory ID" *)
  365.         AddAFPWord(disk, 0);            (* "Bitmap" = request no extra info *)
  366.         AddAFPWord(disk, 1);            (* "Access Mode" = read access *)
  367.         AddAFPByte(disk, 2);            (* "Path Type" = long names *)
  368.         AddAFPString(disk, name);        (* "Pathname" *)
  369.     
  370.         (* Run the command. *)
  371.         disk^.xppParam.rbSize := sizeof(disk^.resultBlock);
  372.         disk^.xppParam.rbPtr := @disk^.resultBlock;
  373.         disk^.xppParam.wdSize := 0;        (* Not used by afpOpenFork *)
  374.         disk^.xppParam.wdPtr := nil;    (* ditto *)
  375.         err := RunAFPCommand(disk, nil);
  376.         
  377.         (* Extract the fork refnum from the two bytes at offset 2 of the result block. *)
  378.         if err = noErr then begin
  379.             disk^.afpForkRef := bsl(disk^.resultBlock[2], 8) + disk^.resultBlock[3];
  380.         end; (* if *)
  381.         
  382.         OpenAFPFork := err;
  383.     end; (* OpenAFPFork *)
  384.     
  385.     function ReadAFPData(disk : DiskImageRecordPtr;
  386.                             offset : longint; count : longint; buffer : Ptr;
  387.                             ioCompletion : ProcPtr) : OSErr;
  388.         (* See comment in implementation part. *)
  389.         var
  390.             err : OSStatus;
  391.     begin
  392.         (* Build the AFP command. *)
  393.         StartAFPCommand(disk, afpRead);
  394.         AddAFPByte(disk, 0);                (* pad *)
  395.         AddAFPWord(disk, disk^.afpForkRef);    (* "OForkRefNum" *)
  396.         AddAFPLong(disk, offset);            (* "Offset" *)
  397.         AddAFPLong(disk, count);            (* "ReqCount" *)
  398.         AddAFPByte(disk, 0);                (* "Newline Mask" *)
  399.         AddAFPByte(disk, 0);                (* "Newline Char" *)
  400.     
  401.         (* Run the command.  Note that XPP special cases afpRead calls,
  402.             handling multiple packet transactions transparently to us.
  403.         *)
  404.         disk^.xppParam.rbPtr := buffer;
  405.         err := RunAFPCommand(disk, ioCompletion);
  406.  
  407.         ReadAFPData := err;
  408.     end; (* ReadAFPData *)
  409.     
  410.     function CloseAFPFork(disk : DiskImageRecordPtr) : OSErr;
  411.         var
  412.             err : OSStatus;
  413.         (* Closes the AFP fork synchronously. *)
  414.     begin
  415.         (* Build the AFP command. *)
  416.         StartAFPCommand(disk, afpForkClose);
  417.         AddAFPByte(disk, 0);                    (* pad *)
  418.         AddAFPWord(disk, disk^.afpForkRef);        (* "OForkRefNum" *)
  419.  
  420.         (* Run the command. *)
  421.         disk^.xppParam.rbSize := 0;
  422.         disk^.xppParam.rbPtr := nil;
  423.         disk^.xppParam.wdSize := 0;                (* Not used by afpForkClose *)
  424.         disk^.xppParam.wdPtr := nil;            (* ditto *)
  425.         err := RunAFPCommand(disk, nil);
  426.         
  427.         CloseAFPFork := err;
  428.     end; (* CloseAFPFork *)
  429.     
  430.     (* ***** Mount ****** *)
  431.     
  432.     function CreateDiskImage(diskImage : FSSpec; var disk : DiskImageRecordPtr) : OSErr;
  433.         (* See comment for ReadDiskImage. *)
  434.         var
  435.             err : OSErr;
  436.             volumeSessionRec: GetVolSessInfoRec;
  437.             header : DiskCopyHeader;
  438.             junk : OSErr;            
  439.     begin
  440.         (* Create the storage for this disk. *)
  441.         disk := DiskImageRecordPtr(NewPtrSysClear(sizeof(DiskImageRecord)));
  442.         err := MemError;
  443.         
  444.         (* Open the data fork of the image using direct AFP calls. *)
  445.         if err = noErr then begin
  446.             err := GetVolSessionInfo(diskImage.vRefNum, volumeSessionRec);
  447.             if err = noErr then begin
  448.                 disk^.aspSessionNumber := volumeSessionRec.sessReferenceNumber;
  449.                 disk^.afpForkRef := 0;
  450.                 err := OpenAFPFork(disk, volumeSessionRec.sessAFPVolID, diskImage.parID, diskImage.name);
  451.             end; (* if *)
  452.         end; (* if *)
  453.         
  454.         (* Now read the image header to calculate diskSize. *)
  455.         if err = noErr then begin
  456.             err := ReadAFPData(disk, 0, sizeof(DiskCopyHeader), @header, nil);
  457.         end; (* if *)
  458.         if err = noErr then begin
  459.             disk^.diskSize := header.dataSize;
  460.         end; (* if *)
  461.         
  462.         (* Clean up. *)        
  463.         if err <> noErr then begin
  464.             junk := DestroyDiskImage(disk);
  465.         end; (* if *)
  466.         
  467.         CreateDiskImage := err;
  468.     end; (* CreateDiskImage *)
  469.     
  470.     (* ***** Read ***** *)
  471.     
  472.     function ReadDiskImage(disk : DiskImageRecordPtr;
  473.                             offset : longint; count : longint; buffer : Ptr;
  474.                             ioCompletion : ProcPtr) : OSErr;
  475.         (* See the implementation part for a description of the external
  476.             interface to this routine.
  477.            This routine exists merely to add 84 to the offset for the read.
  478.             After that it basically calls through to ReadAFPData.  I had
  479.             to do this because I want to call ReadAFPData in CreateDiskImage,
  480.             and I didn't want it to add 84 to the offset.
  481.         *)
  482.     begin
  483.         ReadDiskImage := ReadAFPData(disk,
  484.                                         offset + sizeof(DiskCopyHeader), count, buffer,
  485.                                         ioCompletion);
  486.     end; (* ReadDiskImage *)
  487.     
  488.     (* ***** Unmount ***** *)
  489.  
  490.     function DestroyDiskImage(disk : DiskImageRecordPtr) : OSErr;
  491.         (* See comment in implementation part. *)
  492.         var
  493.             err : OSErr;
  494.     begin
  495.         err := noErr;
  496.         if disk <> nil then begin
  497.             if disk^.afpForkRef <> 0 then begin
  498.                 err := CloseAFPFork(disk);
  499.             end; (* if *)
  500.             DisposePtr(Ptr(disk));
  501.         end; (* if *)
  502.         DestroyDiskImage := err;
  503.     end; (* DestroyDiskImage *)
  504.  
  505. end. (* DiskImageCore *)